using System;
using System.Collections.Generic;
using System.Linq;

namespace Behaviorize.Reporting.Model
{
	internal class Feature
	{
		public string Name { get; set; }
		public string UserStory { get; set; }
		public string Description { get; set; }
		public string LastRun { get; set; }
		public int Passed { get { return Scenarios.Count(sg => sg.Outcome == "Passed"); } }
		public int Pending { get { return Scenarios.Count(sg => sg.Outcome == "Pending"); } }
		public int Failed { get { return Scenarios.Count(sg => sg.Outcome == "Failed"); } }
		public string ClassName { get; set; }
		public List<Scenario> Scenarios { get; set; }

		private ScenarioVersion CreateScenarioVersion(Behaviorize.ScenarioVersion testScenarioVersion, Scenario scenario)
		{
			ScenarioVersion scenarioVersion = new ScenarioVersion
			{
				Steps = new List<Step>(),
				Signature = testScenarioVersion.GetSignature()
			};

			int failureIndexAddend = scenario.FailureMessages.Count;
			AddSteps(scenarioVersion, testScenarioVersion.ArrangementSteps, failureIndexAddend);
			AddSteps(scenarioVersion, testScenarioVersion.ActionSteps, failureIndexAddend);
			AddSteps(scenarioVersion, testScenarioVersion.AssertionSteps, failureIndexAddend);

			foreach (Exception exception in testScenarioVersion.GetExceptions())
			{
				scenario.FailureMessages.Add(exception.Message + "\r\n" + exception.StackTrace);
			}

			return scenarioVersion;
		}

		private static void AddSteps<T>(ScenarioVersion scenarioVersion, List<T> steps, int failureIndexAddend) where T : Behaviorize.Step
		{
			int counter = 0;
			foreach (T testStep in steps)
			{
				Step step = new Step
				{
					Text = ReportMaker.FormatStepText(testStep, counter == 0),
					FailureIndex = testStep.FailureIndex > 0 ? testStep.FailureIndex + failureIndexAddend : 0
				};

				step.SetOutcome(testStep.Outcome);

				scenarioVersion.Steps.Add(step);
				counter++;
			}
		}

		public void CreateOrUpdateScenario(string scenarioName, List<Behaviorize.ScenarioVersion> testScenarios)
		{
			Scenario scenario = Scenarios.SingleOrDefault(sg => sg.Name == scenarioName);

			if (scenario !=null && scenario.NameHashCode != scenarioName.GetHashCode())
			{
				Scenarios.Remove(scenario);
				scenario = null;
			}

			if (scenario == null)
			{
				scenario = new Scenario { Name = scenarioName, NameHashCode = scenarioName.GetHashCode() };
				Scenarios.Add(scenario);
			}

			scenario.FailureMessages = new List<string>();

			foreach (Behaviorize.ScenarioVersion testScenario in testScenarios)
			{
				ScenarioVersion scenarioVersion = CreateScenarioVersion(testScenario, scenario);
				int scenarioIndex = scenario.ScenarioVersions.FindIndex(sc => sc.Signature == scenarioVersion.Signature);

				if (scenarioIndex != -1)
				{
					scenario.ScenarioVersions.RemoveAt(scenarioIndex);
					scenario.ScenarioVersions.Insert(scenarioIndex, scenarioVersion);
				}
				else
				{
					scenario.ScenarioVersions.Add(scenarioVersion);
				}
			}
		}
	}
}